package cn.itsource.service.impl;

import cn.dev33.satoken.stp.SaLoginConfig;
import cn.dev33.satoken.stp.StpUtil;
import cn.itsource.constants.CacheConstants;
import cn.itsource.constants.Constants;
import cn.itsource.constants.ErrorCode;
import cn.itsource.constants.RConst;
import cn.itsource.exception.GlobalException;
import cn.itsource.pojo.domain.Login;
import cn.itsource.mapper.LoginMapper;
import cn.itsource.pojo.domain.Permission;
import cn.itsource.pojo.domain.Role;
import cn.itsource.pojo.dto.LoginDto;
import cn.itsource.pojo.dto.AdminLoginDto;
import cn.itsource.pojo.dto.UpdatePasswordDto;
import cn.itsource.pojo.vo.LoginPreservationVo;
import cn.itsource.result.JSONResult;
import cn.itsource.service.ILoginService;
import cn.itsource.template.WechatTemplate;
import cn.itsource.utils.AssertUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * 登录表 服务实现类
 * </p>
 *
 * @author lifan
 * @since 2024-09-03
 */
@Service
public class LoginServiceImpl extends ServiceImpl<LoginMapper, Login> implements ILoginService {

    @Autowired
    private LoginMapper loginMapper;
    @Autowired
    private WechatTemplate wechatTemplate;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public JSONResult login(LoginDto loginDto) {
        //登录验证的是从前端获取的code拿到opneid，后续和数据库的openid做判断
        String openId = wechatTemplate.getOpenId(loginDto.getCode());
        LambdaQueryWrapper<Login> objectQueryWrapper = new LambdaQueryWrapper<Login>()
                .eq(Login::getOpenId,openId)
                .eq(Login::getType,loginDto.getType());
        //查询出来的登录数据
        Login login = loginMapper.selectOne(objectQueryWrapper);
        if (login != null) {
            //登录成功
            StpUtil.login(login.getId());
            // 将信息存储在session中给前端 jwt无状态的模式下不能使用session
//            StpUtil.getSession().get("session",login);

            // 连缀写法追加多个
            StpUtil.login(login.getId(), SaLoginConfig
                    .setExtra("currentUser",login)); //Constants.CURRENT_LOGIN_KEY;
            // 扩展数据
            LoginPreservationVo build = LoginPreservationVo.builder()
                    .username(login.getUsername())
                    .avatar(login.getAvatar())
                    .nick_name(login.getNickName())
                    .phone(login.getPhone())
                    .token(StpUtil.getTokenValue())
                    .login(login)
                    .build();
            // 把权限&角色信息加入进redis缓存中
            this.getPermissionByUserId(login.getId());
            this.getRoleByUserId(login.getId());
            //返回前端
            return JSONResult.success(build);
        }else {
            throw new GlobalException(ErrorCode.NO_PHONE);
        }
        //getTokenInfo获取登录结果 getTokenValue把Token返回给前端
    }

    @Override
    public List<String> getPermissionByUserId(Long loginId) {

        String key = CacheConstants.CURRENT_USER_PERMISSION+loginId;
        //判断redis中是否有值
        if (redisTemplate.hasKey(key)) {
            //从redis中获取权限列表
            List<String> list = (List<String>) redisTemplate.opsForValue().get(key);
            // redis中有直接返回权限列表
            return list;
        }
        //没有就重新从数据库中获取
        List<Permission> permission= loginMapper.getPermissionByUserId(loginId);
        //查询到的权限集合
        List<String> list = new ArrayList<String>();
        for (Permission permissionItem : permission) {
            list.add(permissionItem.getSn());
        }
        // 在存储进redis中，设置时间12小时清除
        redisTemplate.opsForValue().set(key,list, RConst.Num.INT_12, TimeUnit.HOURS);
        return list;
    }

    @Override
    public List<String> getRoleByUserId(Long loginId) {
        String key = CacheConstants.CURRENT_USER_ROLE+loginId;
        if (redisTemplate.hasKey(key)){
            List<String> list = (List<String>) redisTemplate.opsForValue().get(key);
            return list;
        }
        List<Role> role = loginMapper.getRoleByUserId(loginId);
        //获取集合中的角色
        List<String> list = role.stream().map(Role::getSn).collect(Collectors.toList());
        redisTemplate.opsForValue().set(key,list, RConst.Num.INT_12, TimeUnit.HOURS);
        return list;
    }

    @Override
        public JSONResult adminLogin(AdminLoginDto loginDto) {

            //验证该用户是否存在
            Login login = loginMapper.selectOne(new LambdaQueryWrapper<Login>()
                    .eq(Login::getUsername, loginDto.getUsername())
                    .eq(Login::getType, RConst.Num.BASE_LONG_0));
            AssertUtil.isNotNull(login,"账号或密码错误！");
        boolean matches = passwordEncoder.matches(loginDto.getPassword(), login.getPassword());
        AssertUtil.isTrue(matches,"账号或密码错误！！");
        LoginPreservationVo build = getLoginPreservationVo(login);
        //返回前端
        return JSONResult.success(build);
        }

    @Override
    public JSONResult updatePassword(UpdatePasswordDto updatePasswordDto) {
        if (StpUtil.isLogin()){
            Object currentUser = StpUtil.getExtra("currentUser");
            Login lg = JSON.parseObject(currentUser.toString(), Login.class);
            Login login = loginMapper.selectById(lg.getId());
            boolean matches = passwordEncoder.matches(updatePasswordDto.getOldPassword().trim(), login.getPassword());
            System.out.println(login);
            if (matches){
                //对新密码加密
                String encode = passwordEncoder.encode(updatePasswordDto.getNewPassword());
                login.setPassword(encode);
                loginMapper.updateById(login);
                return JSONResult.success();
            }else {
                throw new GlobalException(ErrorCode.NO_PASS_WORD);
            }
        }
        throw new GlobalException(ErrorCode.NO_LOGIN);
    }

    @Override
    public void loginOut() {
        StpUtil.logout();
    }

    private LoginPreservationVo getLoginPreservationVo(Login login) {
        //密码不能给前端
        login.setPassword(null);
        // 连缀写法追加多个
        StpUtil.login(login.getId(), SaLoginConfig
                .setExtra("currentUser", login));
        // 把权限&角色信息加入进redis缓存中
        List<String> permissionByUserId = this.getPermissionByUserId(login.getId());
        List<String> roleByUserId = this.getRoleByUserId(login.getId());
        // 扩展数据
        LoginPreservationVo build = LoginPreservationVo.builder()
                .username(login.getUsername())
                .avatar(login.getAvatar())
                .nick_name(login.getNickName())
                .phone(login.getPhone())
                .token(StpUtil.getTokenValue())
                .login(login)
                .permissions(permissionByUserId)
                .roles(roleByUserId)
                .build();
        return build;
    }
}
